/**
 * 
 */
package com.aceitunaproject.clientconnectionmanager;

import java.util.HashMap;

import java.util.Calendar;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Timer;
import java.util.TimerTask;
import java.util.UUID;

import com.aceitunaproject.clientinterface.AceitunaClientInterface;

/**
 * Manages a list of connections to the Aceituna Server and periodically checks
 * it for connections that have been unused for a set period of time, when that
 * happens it considers them as expired, sends a logout order and removes them
 * from the list.
 * 
 * @author Federico Bergstein
 * 
 */
public class ClientConnectionManager {

	private ClientInterfaceFactory factory;

	protected Map<UUID, ClientInterfaceHolder> connections = new HashMap<UUID, ClientInterfaceHolder>();
	private long expirationTime = 1000000;
	private int maximumConnectionLimit = 150;
	Timer timer = new Timer();

	/**
	 * Class constructor
	 * 
	 * @param factory
	 *            A factory class to generate AceitunaClientInterface objects.
	 */
	public ClientConnectionManager(ClientInterfaceFactory factory) {

		this.factory = factory;

		// starts the timer that checks the connections last refresh time to see
		// if they should expire.
		timer.schedule(new checkConnectionsTask(), expirationTime,
				expirationTime);
	}

	/**
	 * Creates a new connection to the Aceituna Server.
	 * 
	 * @return the UUID of the new Connection
	 * @throws MaximumConnectionLimitExceeded
	 *             if the maximum connection number has been reached.
	 */
	public UUID createConnection() throws MaximumConnectionLimitExceeded {
		if (connections.size() < maximumConnectionLimit) {
			ClientInterfaceHolder holder = new ClientInterfaceHolder(factory);
			connections.put(holder.getId(), holder);
			return holder.getId();
		} else {
			throw new MaximumConnectionLimitExceeded();
		}
	}

	/**
	 * Gets a connection to the Aceituna Server from the connection list
	 * 
	 * @param id
	 *            the UUID of the connection to return.
	 * @return an Aceituna Client Interface to use as a connection to the
	 *         Aceituna Server.
	 */
	public AceitunaClientInterface getConnection(UUID id) {
		ClientInterfaceHolder holder = connections.get(id);
		if (holder != null) {
			return holder.getClientInterface();
		} else {
			return null;
		}
	}

	
	/**
	 * Gets a ClientInterfaceHolder from the connection list
	 * 
	 * @param id
	 *            the UUID of the connection to return.
	 * @return an Aceituna Client Interface to use as a connection to the
	 *         Aceituna Server.
	 */
	public ClientInterfaceHolder getClientInterfaceHolder(UUID id) {
		return connections.get(id);
		
	}
	/**
	 * Sets the time a connection can exist without being used before expiring.
	 * 
	 * @param milliseconds
	 *            the number of milliseconds in the period.
	 */
	public void setExpirationTime(long milliseconds) {
		expirationTime = milliseconds;

		// cancels the previous timer and schedules again with the new period
		timer.cancel();
		timer = new Timer();
		timer.schedule(new checkConnectionsTask(), 0, expirationTime);

	}

	/**
	 * Sets the maximum number of connections the manager will handle.
	 * 
	 * @param maxConnections
	 *            the number of connections.
	 */
	public void setMaximumConnectionLimit(int maxConnections) {
		maximumConnectionLimit = maxConnections;

	}

	/**
	 * Checks each connection to see if it's expiration time has passed and if
	 * so sends a logout command and removes it from the list connections.
	 * 
	 */
	private class checkConnectionsTask extends TimerTask {

		@Override
		public void run() {
			Iterator<Entry<UUID, ClientInterfaceHolder>> it = connections
					.entrySet().iterator();
			while (it.hasNext()) {
				Entry<UUID, ClientInterfaceHolder> pairs = it.next();
				UUID key = pairs.getKey();
				ClientInterfaceHolder holder = pairs.getValue();

				if (Calendar.getInstance().getTimeInMillis()
						- holder.getLastRefreshTime() > expirationTime) {
					holder.getClientInterface().logout();
					// TODO: not sure if this is necessary
					holder.destroyClientInterface();
					// TODO: not sure if this is necessary
					holder = null;
					connections.remove(key);

				}

			}

		}
	}

}
